import { Tween } from "@tweenjs/tween.js";
import { Camera, OrthographicCamera, PerspectiveCamera, Vector3 } from "three";
import { MapControls, OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { FOV } from "../Util/static";
import { CqkjMap } from "./CqkjMap";

export abstract class Stage {
  abstract readonly mode: string
  readonly map: CqkjMap;
  readonly camera: Camera
  readonly cameras: Camera[]
  readonly control: OrbitControls

  protected _tween?: Tween<number[]>
  protected _stable = true

  constructor(map: CqkjMap) {
    this.map = map
    this.camera = this._createCamera()
    this.cameras = this._createCameras()
    this.control = this._createControl()
    this.control.addEventListener('start', this._onBeforeViewChange.bind(this))
    this.control.addEventListener('change', this._onViewChange.bind(this))
    this.control.addEventListener('end', this._onAfterViewChange.bind(this))
    this._resize(map.renderer.domElement.offsetWidth,map.renderer.domElement.offsetHeight)
  }

  protected abstract _createCamera(): Camera
  protected abstract _createCameras(): Camera[]
  protected abstract _createControl(): OrbitControls

  protected _onBeforeViewChange() {
    const { map } = this
    this._stable = false
    map.mouseHandler.active = false
    map.elements.forEach(e => e.show && e.onBeforeViewChange?.())
  }

  protected _onViewChange() {
    const { map } = this
    map.elements.forEach(e => e.show && e.onViewChange?.())
  }

  protected _onAfterViewChange() {
    const { map } = this
    map.ground.update()
    map.elements.forEach(e => e.show && e.onAfterViewChange?.())
    this._stable = true
    map.mouseHandler.active = true
  }

  render() {
    const { _stable, _tween, map, camera, cameras } = this
    const { renderer, scene } = map


    if (renderer.domElement.parentElement) {
      const container = renderer.domElement.parentElement
      if (container.offsetWidth !== renderer.domElement.offsetWidth || container.offsetHeight !== renderer.domElement.offsetHeight) {
        const width = container.offsetWidth
        const height = container.offsetHeight
        renderer.setSize(width, height)
        this._resize(width, height)
      }
    }
    if (_tween) {
      _tween.update()
    }
    map.elements.forEach(e => e.show && e.onFrame?.())
    renderer.clear()
    if (_stable) {
      cameras.forEach(e => {
        renderer.clearDepth()
        renderer.render(scene, e)
      })
    } else {
      renderer.render(scene, camera)
    }
  }

  protected abstract _resize(w: number, h: number): void

  setView(e: number[], anim = 0) {
    const { camera, control } = this
    this._onBeforeViewChange()
    if (anim) {
      const option = [
        camera.position.x, camera.position.y, camera.position.z,
        control.target.x, control.target.y, control.target.z,
      ]
      this._tween = new Tween(option).to(e, anim).onStart(() => {
        control.enabled = false
      }).onUpdate(() => {
        control.target.set(option[3], option[4], option[5])
        camera.position.set(option[0], option[1], option[2])
        camera.lookAt(control.target)
        camera.updateMatrixWorld()
        this._onViewChange()
      }).onComplete(() => {

        control.saveState()
        control.enabled = true
        this._tween!.stop()
        this._tween = undefined
        this._onAfterViewChange()
      }).start()
    } else {
      control.target.set(e[3], e[4], e[5])
      camera.position.set(e[0], e[1], e[2])
      camera.lookAt(control.target)
      control.saveState()
      camera.updateMatrixWorld()
      this._onViewChange()
      this._onAfterViewChange()
    }

  }

  destroy(){
    this.control.dispose()
  }
}

const tempV3=new Vector3()
export class Stage2D extends Stage {
  readonly mode = '2D'
  protected _resize(w: number, h: number): void {
    console.log(w,h);
    
  }
  protected _createCamera(): Camera {
    const camera = new OrthographicCamera(-1, 1, 1, -1, 100, 70000000)
    camera.up = new Vector3(0, 1, 0)
    return camera
  }
  protected _createCameras(): Camera[] {
    const { matrixWorld, matrixWorldInverse } = this.camera

    const cameras = [
      new OrthographicCamera(-1, 1, 1, -1, 10000000, 70000000),
      new OrthographicCamera(-1, 1, 1, -1, 1000000, 10000000),
      new OrthographicCamera(-1, 1, 1, -1, 100, 1000000)
    ]
    cameras.forEach(e => {
      e.matrixAutoUpdate = false
      e.matrixWorldAutoUpdate = false
      e.matrixWorld = matrixWorld
      e.matrixWorldInverse = matrixWorldInverse
    })
    return cameras
  }
  protected _onViewChange() {
    const { map,control } = this
    const camera = this.camera as OrthographicCamera
    // const cameras = this.cameras as OrthographicCamera[]
    tempV3.copy(camera.position).sub(control.target).divideScalar(camera.zoom)
    const l=tempV3.length()

   
    const h = l / 2
    const w = h * map.renderer.domElement.offsetWidth/map.renderer.domElement.offsetHeight
    camera.left = -w
    camera.right = w
    camera.top = h
    camera.bottom = -h
    camera.zoom=1
    camera.updateProjectionMatrix()
    camera.position.copy(tempV3.add(control.target))
    camera.updateMatrixWorld()
    super._onViewChange()
  }
  protected _onAfterViewChange(): void {
    const camera = this.camera as OrthographicCamera
    const cameras = this.cameras as OrthographicCamera[]
     cameras.forEach(e => {
      e.left = camera.left
      e.right = camera.right
      e.top = camera.top
      e.bottom = camera.bottom
      e.updateProjectionMatrix()
    })
    super._onAfterViewChange()
  }
  protected _createControl(): OrbitControls {
    const { camera, map } = this
    const control = new MapControls(camera, map.renderer.domElement)
    control.screenSpacePanning=true
    control.enableRotate=false
    return control
  }


}

export class Stage3D extends Stage {
  readonly mode = '3D'
  protected _resize(w: number, h: number): void {
    const camera = this.camera as PerspectiveCamera
    const cameras = this.cameras as PerspectiveCamera[]
    camera.aspect = w / h
    camera.updateProjectionMatrix()
    cameras.forEach(e => {
      e.aspect = camera.aspect
      e.updateProjectionMatrix()
    })
  }
  protected _createCamera(): Camera {
    const camera = new PerspectiveCamera(FOV, 2, 100, 70000000)
    camera.up = new Vector3(0, 0, 1)
    return camera
  }
  protected _createCameras(): Camera[] {
    const { matrixWorld, matrixWorldInverse } = this.camera

    const cameras = [
      new PerspectiveCamera(FOV, 2, 10000000, 70000000),
      new PerspectiveCamera(FOV, 2, 1000000, 10000000),
      new PerspectiveCamera(FOV, 2, 100, 1000000)
    ]
    cameras.forEach(e => {
      e.matrixAutoUpdate = false
      e.matrixWorldAutoUpdate = false
      e.matrixWorld = matrixWorld
      e.matrixWorldInverse = matrixWorldInverse
    })
    return cameras
  }
  protected _createControl(): OrbitControls {
    const { camera, map } = this
    const control = new MapControls(camera, map.renderer.domElement)
    control.maxPolarAngle = 1.5
    control.maxDistance = 30000000
    return control
  }

}